/*
 * Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.huawei.cloudphone.virtualdevice.microphone;

import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_MICROPHONE;
import static com.huawei.cloudphone.jniwrapper.JNIWrapper.MICROPHONE_DATA;
import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol.MSG_HEADER_LEN;
import static com.huawei.cloudphone.virtualdevice.microphone.VirtualMicrophone.CHANNEL;
import static com.huawei.cloudphone.virtualdevice.microphone.VirtualMicrophone.FORMAT;
import static com.huawei.cloudphone.virtualdevice.microphone.VirtualMicrophone.MAX_FRAME_SIZE;
import static com.huawei.cloudphone.virtualdevice.microphone.VirtualMicrophone.SAMPLE_RATE;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import androidx.core.app.ActivityCompat;

import com.huawei.cloudphone.api.CloudPhonePermissionInfo;
import com.huawei.cloudphone.api.CloudPhonePermissionRequestListener;
import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol.MsgHeader;

import java.util.HashMap;
import java.util.Map;

public class VirtualMicrophoneManager extends VirtualDeviceManager {
    private static final String TAG = "VirtualMICManager";
    private static final short DEV_MIC_ID = 0;

    private static final short OPT_MICROPHONE_START_RECORD_REQ = 0x1;
    private static final short OPT_MICROPHONE_START_RECORD_RSP = 0x1001;
    private static final short OPT_MICROPHONE_STOP_RECORD_REQ = 0x2;
    private static final short OPT_MICROPHONE_STOP_RECORD_RSP = 0x1002;
    private static final short OPT_MICROPHONE_SET_PARAM_REQ = 0x3;
    private static final short OPT_MICROPHONE_SET_PARAM_RSP = 0x1003;
    private static final short OPT_MICROPHONE_DATA = 0x10;

    private static final int RSP_RESULT_LENGTH = 2;
    private static final int RSP_AUDIO_TYPE_LENGTH = 2;

    private VirtualMicrophone mVirtualMicrophone;
    private VirtualDeviceProtocol mVirtualDeviceProtocol;
    private Context mContext;
    private CloudPhonePermissionRequestListener mPermissionListener;

    public VirtualMicrophoneManager(VirtualDeviceProtocol virtualDeviceProtocol, Context context) {
        mVirtualDeviceProtocol = virtualDeviceProtocol;
        mVirtualMicrophone = new VirtualMicrophone();
        mContext = context;
    }

    @Override
    public void setPermissionListener(CloudPhonePermissionRequestListener listener) {
        mPermissionListener = listener;
    }

    public void processMsg(MsgHeader header, byte[] msgBody) {
        switch (header.mOptType) {
            case OPT_MICROPHONE_START_RECORD_REQ:
                Log.i(TAG, "processMsg: start record");
                handleStartRecordReq(msgBody);
                break;
            case OPT_MICROPHONE_STOP_RECORD_REQ:
                Log.i(TAG, "processMsg: stop record");
                handleStopRecordReq(msgBody);
                break;
            case OPT_MICROPHONE_SET_PARAM_REQ:
                Log.i(TAG, "processMsg: set param");
                handleSetParamReq(msgBody);
                break;
        }
    }

    public void init() {
        mVirtualMicrophone.setOnRecvDataListener(new MicrophoneDataListener());
        int result = mVirtualMicrophone.start();
        if (result != 0) {
            Log.e(TAG, "initMicrophone: failed to start microphone");
        }
        byte[] rspBody = new byte[RSP_RESULT_LENGTH + RSP_AUDIO_TYPE_LENGTH];
        rspBody[0] = 0x0;
        rspBody[1] = (byte) (result == 0 ? 0x0 : 0x1);
        rspBody[2] = 0x00;
        rspBody[3] = 0x01;
        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH + RSP_AUDIO_TYPE_LENGTH;
        MsgHeader rspHeader = new MsgHeader(OPT_MICROPHONE_START_RECORD_RSP, DEV_TYPE_MICROPHONE, DEV_MIC_ID, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, MICROPHONE_DATA);
    }

    private void handleStartRecordReq(byte[] msgBody) {
        Context context = mContext.getApplicationContext();
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
        if (context.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            if (mPermissionListener == null) {
                ActivityCompat.requestPermissions((Activity) mContext,
                        new String[]{Manifest.permission.RECORD_AUDIO},
                        DEV_TYPE_MICROPHONE);
            } else {
                mPermissionListener.onRequestPermissions(new CloudPhonePermissionInfo(
                        DEV_TYPE_MICROPHONE, new String[]{Manifest.permission.RECORD_AUDIO}));
            }
        } else {
            init();
        }
    }

    private void handleStopRecordReq(byte[] msgBody) {
        int result = mVirtualMicrophone.stop();
        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH;
        byte[] rspBody = new byte[RSP_RESULT_LENGTH];
        rspBody[0] = 0x0;
        rspBody[1] = (byte) (result == 0 ? 0x0 : 0x1);
        MsgHeader rspHeader = new MsgHeader(OPT_MICROPHONE_STOP_RECORD_RSP, DEV_TYPE_MICROPHONE, DEV_MIC_ID, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, MICROPHONE_DATA);
    }

    private void handleSetParamReq(byte[] msgBody) {
        int maxFrameSize = (msgBody[0] << 8) | (msgBody[1] & 0x0FF);
        int format = (msgBody[2] << 8) | (msgBody[3] & 0x0FF);
        int sampleRate = (msgBody[4] & 0xFF) << 8 | (msgBody[5] & 0xFF);
        int channel = (msgBody[6] << 8) | (msgBody[7] & 0x0FF);
        Log.i(TAG, "mSampleRate = " + sampleRate + ", mMaxFrameSize = " + maxFrameSize +
                ", mChannel = " + channel + ", mSampleFormat = " + format);

        Map<String, String> paramMap = new HashMap<>();
        paramMap.put(FORMAT, Integer.toString(format));
        paramMap.put(MAX_FRAME_SIZE, Integer.toString(maxFrameSize));
        paramMap.put(SAMPLE_RATE, Integer.toString(sampleRate));
        paramMap.put(CHANNEL, Integer.toString(channel));

        int result = mVirtualMicrophone.setParameters(paramMap);
        byte[] rspBody = new byte[RSP_RESULT_LENGTH];
        rspBody[0] = 0x0;
        rspBody[1] = (byte) (result == 0 ? 0x0 : 0x1);

        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH;
        MsgHeader header = new MsgHeader(OPT_MICROPHONE_SET_PARAM_RSP, DEV_TYPE_MICROPHONE, DEV_MIC_ID, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(header, rspBody, MICROPHONE_DATA);
    }

    public void stop() {
        mVirtualMicrophone.stop();
    }

    public class MicrophoneDataListener implements IVirtualDeviceDataListener {

        @Override
        public void onRecvData(Object... args) {
            byte[] data = (byte[]) args[0];
            int offset = (int) args[1];
            int length = (int) args[2];
            int rspMsgLen = length + MSG_HEADER_LEN;
            MsgHeader header = new MsgHeader(OPT_MICROPHONE_DATA, DEV_TYPE_MICROPHONE, DEV_MIC_ID, rspMsgLen);
            byte[] rspBody = new byte[length];
            System.arraycopy(data, offset, rspBody, 0, length);
            mVirtualDeviceProtocol.sendMsg(header, rspBody, MICROPHONE_DATA);
        }
    }
}
